/*
 * Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "ScriptPCH.h"
#include "oculus.h"
#include "MapManager.h"

enum Says
{
    SAY_AGGRO       = 1,
    SAY_DEATH       = 2,
    SAY_AIR_STRIKE  = 3,
    SAY_KILL        = 4,
    SAY_SPAWN       = 5
};

enum Spells
{
    SPELL_ENERGIZE_CORES_VISUAL       = 62136,
    SPELL_ENERGIZE_CORES              = 50785, // Damage 5938 to 6562, effec2 Triggers 54069, effect3 Triggers 56251
    SPELL_ENERGIZE_CORES_H            = 59372,
    SPELL_CALL_AZURE_RING_CAPTAIN     = 51002, // Effect Send Event (12229)
    /*SPELL_CALL_AZURE_RING_CAPTAIN_2 = 51006, // Effect Send Event (10665)
    SPELL_CALL_AZURE_RING_CAPTAIN_3   = 51007, // Effect Send Event (18454)
    SPELL_CALL_AZURE_RING_CAPTAIN_4   = 51008, // Effect Send Event (18455)*/
    SPELL_CALL_AMPLIFY_MAGIC          = 51054,
    SPELL_CALL_AMPLIFY_MAGIC_H        = 59371,

    SPELL_ARCANE_BEAM_PERIODIC        = 51019,
    SPELL_SUMMON_ARCANE_BEAM          = 51017,
    SPELL_ARCANE_BEAM_VISUAL          = 51024,
    SPELL_CENTRIFUGE_CORE_PASSIVE     = 50798
};

enum Events
{
    EVENT_ENERGIZE_CORES = 1,
    EVENT_CALL_AZURE,
    EVENT_AMPLIFY_MAGIC,
    EVENT_ENERGIZE_CORES_VISUAL
};

static Position corePositions[]=
{
    {1305.189f, 1029.883f, 438.942f, 0.0f},
    {1276.570f, 1026.267f, 438.942f, 0.0f},
    {1245.383f, 1050.026f, 438.942f, 0.0f},
    {1241.266f, 1080.340f, 438.942f, 0.0f},
    {1264.629f, 1110.341f, 438.942f, 0.0f},
    {1295.459f, 1114.321f, 438.942f, 0.0f},
    {1325.323f, 1091.365f, 438.942f, 0.0f},
    {1329.289f, 1060.288f, 438.942f, 0.0f},
};

class boss_varos : public CreatureScript
{
    public:
        boss_varos() : CreatureScript("boss_varos") { }

        struct boss_varosAI : public BossAI
        {
            boss_varosAI(Creature* creature) : BossAI(creature, DATA_VAROS_EVENT)
            {
                if (instance->GetBossState(DATA_DRAKOS_EVENT) != DONE)
                    DoCast(me, SPELL_CENTRIFUGE_SHIELD);
            }

            void Reset()
            {
                _Reset();

                events.ScheduleEvent(EVENT_AMPLIFY_MAGIC, urand(20, 25) * IN_MILLISECONDS);
                events.ScheduleEvent(EVENT_ENERGIZE_CORES_VISUAL, 5000);
                // not sure if this is handled by a timer or hp percentage
                events.ScheduleEvent(EVENT_CALL_AZURE, urand(15, 30) * IN_MILLISECONDS);

                energizedCore = 0; // clockwise?

                _spawntextTimer = urand (1, 60) *IN_MILLISECONDS;
            }

            void EnterCombat(Unit* /*who*/)
            {
                _EnterCombat();
                Talk(SAY_AGGRO);
                SpawnCores();
            }

            void JustSummoned(Creature* summon)
            {
                summons.Summon(summon);
            }

            float GetCoreEnergizeOrientation(bool first)
            {
                uint8 mod = first ? energizedCore : energizedCore + 3;
                if (mod >= 8)
                    mod -= 8;

                float angle = me->GetAngle(corePositions[mod].GetPositionX(), corePositions[mod].GetPositionY());
                angle += first ? 0.2f : -0.2f;
                angle = MapManager::NormalizeOrientation(angle);

                return angle;
            }

            void SpawnCores()
            {
                for (uint8 i = 0; i < 8; ++i)
                    if (Creature* core = me->SummonCreature(NPC_CENTRIFUGE_CORE, corePositions[i]))
                    {
                        core->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_NPC);
                        core->SetDisplayId(core->GetCreatureTemplate()->Modelid2);
                        core->AddAura(SPELL_CENTRIFUGE_CORE_PASSIVE, core);
                    }
            }

            void UpdateAI(uint32 const diff)
            {
                if (!me->isInCombat() && me->isAlive())
                {
                    if (_spawntextTimer <= diff)
                    {
                        Talk(SAY_SPAWN);
                        _spawntextTimer = 60 *IN_MILLISECONDS;
                    }
                    else
                        _spawntextTimer -= diff;
                }

                if (!UpdateVictim())
                    return;

                events.Update(diff);
                _DoAggroPulse(diff);

                if (me->HasUnitState(UNIT_STATE_CASTING))
                    return;

                while (uint32 eventId = events.ExecuteEvent())
                {
                    switch (eventId)
                    {
                        case EVENT_ENERGIZE_CORES:
                            DoCast(me, DUNGEON_MODE(SPELL_ENERGIZE_CORES, SPELL_ENERGIZE_CORES_H));
                            break;
                        case EVENT_ENERGIZE_CORES_VISUAL:
                            energizedCore += 2;
                            if (energizedCore >= 8)
                                energizedCore -= 8;
                            DoCast(me, SPELL_ENERGIZE_CORES_VISUAL);
                            events.ScheduleEvent(EVENT_ENERGIZE_CORES_VISUAL, 5000);
                            events.ScheduleEvent(EVENT_ENERGIZE_CORES, 4000);
                            break;
                        case EVENT_CALL_AZURE:
                            DoCast(me, SPELL_CALL_AZURE_RING_CAPTAIN);
                            Talk(SAY_AIR_STRIKE);
                            events.ScheduleEvent(EVENT_CALL_AZURE, urand(20, 25) * IN_MILLISECONDS);
                            break;
                        case EVENT_AMPLIFY_MAGIC:
                            DoCastVictim(DUNGEON_MODE(SPELL_CALL_AMPLIFY_MAGIC, SPELL_CALL_AMPLIFY_MAGIC_H));
                            events.ScheduleEvent(EVENT_AMPLIFY_MAGIC, urand(17, 20) * IN_MILLISECONDS);
                            break;
                    }
                }

                DoMeleeAttackIfReady();
            }

            void JustDied(Unit* /*killer*/)
            {
                Talk(SAY_DEATH);
                _JustDied();
                DoCast(me, SPELL_DEATH_SPELL, true);
            }

            void KilledUnit(Unit* /*victim*/)
            {
                Talk(SAY_KILL);
            }

        private:
            uint8 energizedCore;
            uint32 _spawntextTimer;
        };

        CreatureAI* GetAI(Creature* creature) const
        {
            return new boss_varosAI(creature);
        }
};

class npc_azure_ring_captain : public CreatureScript
{
    public:
        npc_azure_ring_captain() : CreatureScript("npc_azure_ring_captain") { }

        struct npc_azure_ring_captainAI : public ScriptedAI
        {
            npc_azure_ring_captainAI(Creature* creature) : ScriptedAI(creature)
            {
                instance = creature->GetInstanceScript();
            }

            void Reset()
            {
                targetGUID = 0;

                me->SetWalk(true);
                me->AddUnitMovementFlag(MOVEMENTFLAG_FLYING);
                me->SetReactState(REACT_AGGRESSIVE);
            }

            void UpdateAI(uint32 const /*diff*/)
            {
                if (!UpdateVictim())
                    return;

                DoMeleeAttackIfReady();
            }

            void MovementInform(uint32 type, uint32 id)
            {
                if (type != POINT_MOTION_TYPE || id != ACTION_CALL_DRAGON_EVENT)
                    return;

                me->GetMotionMaster()->MoveIdle();

                if (Unit* target = ObjectAccessor::GetUnit(*me, targetGUID))
                {
                    if (Creature* summoned = target->SummonCreature(28239, *target, TEMPSUMMON_TIMED_DESPAWN, 10000))
                    {
                        summoned->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED);
                        DoCast(summoned, SPELL_ARCANE_BEAM_VISUAL);
                    }
                }
            }

            void DoAction(int32 const action)
            {
                switch (action)
                {
                   case ACTION_CALL_DRAGON_EVENT:
                        if (instance)
                        {
                            if (Creature* varos = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_VAROS)))
                            {
                                if (Unit* victim = varos->AI()->SelectTarget(SELECT_TARGET_RANDOM, 0))
                                {
                                    me->DespawnOrUnsummon(20*IN_MILLISECONDS);
                                    me->SetReactState(REACT_PASSIVE);
                                    me->SetWalk(false);
                                    me->GetMotionMaster()->MovePoint(ACTION_CALL_DRAGON_EVENT, victim->GetPositionX(), victim->GetPositionY(), victim->GetPositionZ() + 20.0f);
                                    targetGUID = victim->GetGUID();
                                }
                            }
                        }
                        break;
                }
            }

        private:
            uint64 targetGUID;
            InstanceScript* instance;
        };

        CreatureAI* GetAI(Creature* creature) const
        {
            return new npc_azure_ring_captainAI(creature);
        }
};

class spell_varos_centrifuge_shield : public SpellScriptLoader
{
    public:
        spell_varos_centrifuge_shield() : SpellScriptLoader("spell_varos_centrifuge_shield") { }

        class spell_varos_centrifuge_shield_AuraScript : public AuraScript
        {
            PrepareAuraScript(spell_varos_centrifuge_shield_AuraScript);

            bool Load()
            {
                Unit* caster = GetCaster();
                return (caster && caster->ToCreature());
            }

            void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
            {
                if (Unit* caster = GetCaster())
                {
                    // flags taken from sniffs
                    if (caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_15|UNIT_FLAG_IMMUNE_TO_NPC|UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_UNK_6))
                    {
                        caster->ToCreature()->SetReactState(REACT_PASSIVE);
                        caster->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_15|UNIT_FLAG_IMMUNE_TO_NPC|UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_UNK_6);
                    }
                }
            }

            void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
            {
                if (Unit* caster = GetCaster())
                {
                    caster->ToCreature()->SetReactState(REACT_DEFENSIVE);
                    caster->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_15|UNIT_FLAG_IMMUNE_TO_NPC|UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_UNK_6);
                }
            }

            void Register()
            {
                OnEffectRemove += AuraEffectRemoveFn(spell_varos_centrifuge_shield_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
                OnEffectApply += AuraEffectApplyFn(spell_varos_centrifuge_shield_AuraScript::OnApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
            }
        };

        AuraScript* GetAuraScript() const
        {
            return new spell_varos_centrifuge_shield_AuraScript();
        }
};

class spell_varos_energize_core_area_enemy : public SpellScriptLoader
{
    public:
        spell_varos_energize_core_area_enemy() : SpellScriptLoader("spell_varos_energize_core_area_enemy") {}

        class spell_varos_energize_core_area_enemySpellScript : public SpellScript
        {
            PrepareSpellScript(spell_varos_energize_core_area_enemySpellScript)

            void FilterTargets(std::list<Unit*>& targetList)
            {
                Creature* varos = GetCaster()->ToCreature();
                if (!varos)
                    return;

                if (varos->GetEntry() != NPC_VAROS)
                    return;

                float lborder = CAST_AI(boss_varos::boss_varosAI, varos->AI())->GetCoreEnergizeOrientation(true);
                float rborder = CAST_AI(boss_varos::boss_varosAI, varos->AI())->GetCoreEnergizeOrientation(false);
                std::list<Unit*> tempList;

                for (std::list<Unit*>::iterator itr = targetList.begin(); itr != targetList.end(); ++itr)
                {
                    float angle = varos->GetAngle((*itr)->GetPositionX(), (*itr)->GetPositionY());

                    if (lborder < rborder)
                    {
                        if (angle >= lborder && angle <= rborder)
                            continue;
                    }
                    else if (angle >= lborder || angle <= rborder)
                        continue;

                    tempList.push_back(*itr);
                }

                targetList = tempList;
            }

            void Register()
            {
                OnUnitTargetSelect += SpellUnitTargetFn(spell_varos_energize_core_area_enemySpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
            }
        };

        SpellScript* GetSpellScript() const
        {
            return new spell_varos_energize_core_area_enemySpellScript();
        }
};

class spell_varos_energize_core_area_entry : public SpellScriptLoader
{
    public:
        spell_varos_energize_core_area_entry() : SpellScriptLoader("spell_varos_energize_core_area_entry") {}

        class spell_varos_energize_core_area_entrySpellScript : public SpellScript
        {
            PrepareSpellScript(spell_varos_energize_core_area_entrySpellScript)

            void FilterTargets(std::list<Unit*>& targetList)
            {
                Creature* varos = GetCaster()->ToCreature();
                if (!varos)
                    return;

                if (varos->GetEntry() != NPC_VAROS)
                    return;

                float lborder = CAST_AI(boss_varos::boss_varosAI, varos->AI())->GetCoreEnergizeOrientation(true);
                float rborder = CAST_AI(boss_varos::boss_varosAI, varos->AI())->GetCoreEnergizeOrientation(false);
                std::list<Unit*> tempList;

                for (std::list<Unit*>::iterator itr = targetList.begin(); itr != targetList.end(); ++itr)
                {
                    float angle = varos->GetAngle((*itr)->GetPositionX(), (*itr)->GetPositionY());

                    if (lborder < rborder)
                    {
                        if (angle >= lborder && angle <= rborder)
                            continue;
                    }
                    else if (angle >= lborder || angle <= rborder)
                        continue;

                    tempList.push_back(*itr);
                }

                targetList = tempList;
            }

            void Register()
            {
                OnUnitTargetSelect += SpellUnitTargetFn(spell_varos_energize_core_area_entrySpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY);
            }
        };

        SpellScript* GetSpellScript() const
        {
            return new spell_varos_energize_core_area_entrySpellScript();
        }
};

void AddSC_boss_varos()
{
    new boss_varos();
    new npc_azure_ring_captain();
    new spell_varos_centrifuge_shield();
    new spell_varos_energize_core_area_enemy();
    new spell_varos_energize_core_area_entry();
}
